package com.jinmdz.fmis.core.license;

import com.jinmdz.fmis.core.util.JacksonUtil;
import com.jinmdz.fmis.core.util.ProcessUtil;
import de.schlichtherle.license.*;
import de.schlichtherle.xml.GenericCertificate;
import de.schlichtherle.xml.XMLConstants;

import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义LicenseManager，用于增加额外的信息校验(除了LicenseManager的校验，在这个类里面添加额外的校验信息)
 * (源自网络)
 *
 * @author LiCongLu
 * @date 2020-01-08 15:12
 */
public class CertificateLicenseManager extends LicenseManager {

    /**
     * 验证日期监听
     */
    private OnNowDateListener dateListener;

    /**
     * 许可证内容
     */
    private LicenseContent licenseContent;

    /**
     * 扩展数据
     */
    private CertificateExtraModel extraModel;

    /**
     * 验证信息
     */
    private String message;

    public CertificateLicenseManager(LicenseParam param, OnNowDateListener dateListener) {
        super(param);
        this.dateListener = dateListener;
    }

    @Override
    protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception {
        initialize(content);
        this.validateCreate(content);
        final GenericCertificate certificate = notary.sign(content);
        return getPrivacyGuard().cert2key(certificate);
    }

    /**
     * 复写install方法，其中validate方法调用本类中的validate方法，校验IP地址、Mac地址等其他信息
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-01-08 15:34
     */
    @Override
    protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception {
        final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
        notary.verify(certificate);
        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
        this.validate(content);
        setLicenseKey(key);
        setCertificate(certificate);
        return content;
    }

    /**
     * 复写verify方法，调用本类中的validate方法，校验IP地址、Mac地址等其他信息
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-01-08 15:34
     */
    @Override
    protected synchronized LicenseContent verify(final LicenseNotary notary) throws Exception {
        // Load license key from preferences
        final byte[] key = getLicenseKey();
        if (null == key) {
            throw new NoLicenseInstalledException(getLicenseParam().getSubject());
        }

        GenericCertificate certificate = getPrivacyGuard().key2cert(key);
        notary.verify(certificate);
        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
        this.validate(content);
        setCertificate(certificate);

        return content;
    }

    /**
     * 校验生成证书的参数信息
     *
     * @param content
     * @return
     * @author LiCongLu
     * @date 2020-01-08 15:35
     */
    protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException {
        final LicenseParam param = getLicenseParam();
        final Date now = dateListener != null ? dateListener.onNowDate() : new Date();
        final Date notBefore = content.getNotBefore();
        final Date notAfter = content.getNotAfter();
        if (null != notAfter && now.after(notAfter)) {
            message = "证书失效时间不能早于当前时间";
            throw new LicenseContentException(message);
        }
        if (null != notBefore && null != notAfter && notAfter.before(notBefore)) {
            message = "证书生效时间不能晚于证书失效时间";
            throw new LicenseContentException(message);
        }
        final String consumerType = content.getConsumerType();
        if (null == consumerType) {
            message = "用户类型不能为空";
            throw new LicenseContentException(message);
        }
    }

    /**
     * 当前日期传递监听
     *
     * @param
     * @author LiCongLu
     * @return
     * @date 2020-04-16 16:52
     */
    public interface OnNowDateListener {
        /**
         * 传递当前参数
         *
         * @param
         * @return
         * @author LiCongLu
         * @date 2020-04-16 16:52
         */
        default Date onNowDate() {
            return new Date();
        }
    }

    /**
     * 复写validate方法
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-01-08 15:35
     */
    @Override
    protected synchronized void validate(final LicenseContent content) throws LicenseContentException {
        // 记录内容
        licenseContent = content;
        // 记录日期
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        message = MessageFormat.format("许可证书有效期，从【{0}】截止到【{1}】", format.format(licenseContent.getNotBefore()), format.format(licenseContent.getNotAfter()));

        //1. 然后校验自定义的License参数，去校验我们的license信息
        validateCreate(content);
        //2. 进行自定义的校验
        Object extra = content.getExtra();
        // 判断扩展
        if (extra != null && extra instanceof String) {
            extraModel = JacksonUtil.json2Obj(String.valueOf(extra), CertificateExtraModel.class);
            if (extraModel != null) {
                // 获取本机处理器ID
                String processorId = ProcessUtil.getProcessorId();
                // 能够获取本机处理ID时才进行验证
                if (processorId != null && processorId.length() > 0) {
                    if (!processorId.equals(extraModel.getProcessorId())) {
                        message = "认证CPU序列号错误";
                        throw new LicenseContentException(message);
                    }
                }
            }
        }

        //3. 最后调用父类的validate方法
        super.validate(content);
    }

    /**
     * 重写XMLDecoder解析XML
     *
     * @param encoded
     * @return
     * @author LiCongLu
     * @date 2020-01-08 15:35
     */
    private Object load(String encoded) {
        BufferedInputStream inputStream = null;
        XMLDecoder decoder = null;
        try {
            inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XMLConstants.XML_CHARSET)));
            decoder = new XMLDecoder(new BufferedInputStream(inputStream, XMLConstants.DEFAULT_BUFSIZE), null, null);
            return decoder.readObject();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } finally {
            try {
                if (decoder != null) {
                    decoder.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public LicenseContent getLicenseContent() {
        return licenseContent;
    }

    public CertificateExtraModel getExtraModel() {
        return extraModel;
    }

    public String getMessage() {
        return message;
    }
}